package com.sunday.common.mongo.query;


import com.sunday.common.core.lambda.toolkit.support.SFunction;
import com.sunday.common.mongo.core.MongoFieldMapping;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.mongodb.core.query.Criteria;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Stream;

/**
 * 条件 Wrapper
 * 额外条件 可以选择直接使用原生的 Criteria 进行编辑
 *
 * @author zsy
 * @since 2022/10/29
 */
@Slf4j
public class CriteriaWrapper {

    @Getter
    private Criteria criteria;

    public CriteriaWrapper() {
        this.criteria = new Criteria();
    }

    public CriteriaWrapper(Criteria criteria) {
        assert criteria != null : "criteria must not be null";
        this.criteria = criteria;
    }

    public static CriteriaWrapper where() {
        return new CriteriaWrapper();
    }

    /**
     * ==  {_id:115469}
     */
    public <T> CriteriaWrapper eq(SFunction<T, ?> column, Object value) {
        criteria.and(MongoFieldMapping.getColumnName(column)).is(value);
        return this;
    }

    /**
     * !=  {_id:{$ne:115469}}
     */
    public <T> CriteriaWrapper ne(SFunction<T, ?> column, Object value) {
        criteria.and(MongoFieldMapping.getColumnName(column)).ne(value);
        return this;
    }

    /**
     * <  {_id:{$lt:115470}}
     */
    public <T> CriteriaWrapper lt(SFunction<T, ?> column, Object value) {
        criteria.and(MongoFieldMapping.getColumnName(column)).lt(value);
        return this;
    }

    /**
     * <=  {_id:{$lte:115468}}
     */
    public <T> CriteriaWrapper lte(SFunction<T, ?> column, Object value) {
        criteria.and(MongoFieldMapping.getColumnName(column)).lte(value);
        return this;
    }

    /**
     * >  {_id:{$gt:115468}}
     */
    public <T> CriteriaWrapper gt(SFunction<T, ?> column, Object value) {
        criteria.and(MongoFieldMapping.getColumnName(column)).gt(value);
        return this;
    }

    /**
     * >=  {_id:{$gte:115468}}
     */
    public <T> CriteriaWrapper gte(SFunction<T, ?> column, Object value) {
        criteria.and(MongoFieldMapping.getColumnName(column)).gte(value);
        return this;
    }

    /**
     * in  {_id:{$in:[115468]}}
     */
    public <T> CriteriaWrapper in(SFunction<T, ?> column, Object... values) {
        criteria.and(MongoFieldMapping.getColumnName(column)).in(arrayToCollection(values));
        return this;
    }

    /**
     * not in  {_id:{$nin:[115469]}}
     */
    public <T> CriteriaWrapper nin(SFunction<T, ?> column, Object... values) {
        criteria.and(MongoFieldMapping.getColumnName(column)).nin(arrayToCollection(values));
        return this;
    }

    /**
     * %value%  {personnel_number:{'$regex' : '.*fangcaodi.*'}}
     */
    public <T> CriteriaWrapper lk(SFunction<T, ?> column, Object value) {
        criteria.and(MongoFieldMapping.getColumnName(column)).regex(".*" + value + ".*");
        return this;
    }

    /**
     * value%  {personnel_number:{'$regex' : '^fang'}}
     */
    public <T> CriteriaWrapper lfk(SFunction<T, ?> column, Object value) {
        criteria.and(MongoFieldMapping.getColumnName(column)).regex("^" + value);
        return this;
    }

    /**
     * %value  {personnel_number:{'$regex' : 'caodi$'}}
     */
    public <T> CriteriaWrapper rhk(SFunction<T, ?> column, Object value) {
        criteria.and(MongoFieldMapping.getColumnName(column)).regex(value + "$");
        return this;
    }

    /**
     * < and >  {_id:{$lt:309898,$gt:309896}}
     */
    public <T> CriteriaWrapper ltGt(SFunction<T, ?> column, Object start, Object end) {
        criteria.and(MongoFieldMapping.getColumnName(column)).lt(start).gt(end);
        return this;
    }

    /**
     * > and <  {created_time:{$gt:ISODate('2017-05-27 17:49:29.000'),$lt:ISODate('2017-05-27 17:49:31.000')}}
     */
    public <T> CriteriaWrapper gtLt(SFunction<T, ?> column, Object start, Object end) {
        criteria.and(MongoFieldMapping.getColumnName(column)).gt(start).lt(end);
        return this;
    }

    /**
     * <= and =>  {created_time:{$lte:ISODate('2017-05-27 17:49:31.000'),$gte:ISODate('2017-05-27 17:49:29.000')}}
     */
    public <T> CriteriaWrapper lteGte(SFunction<T, ?> column, Object start, Object end) {
        criteria.and(MongoFieldMapping.getColumnName(column)).lte(start).gte(end);
        return this;
    }

    /**
     * >= and <=  {created_time:{$gte:ISODate('2017-05-27 17:49:29.000'),$lte:ISODate('2017-05-27 17:49:31.000')}}
     */
    public <T> CriteriaWrapper gteLte(SFunction<T, ?> column, Object start, Object end) {
        criteria.and(MongoFieldMapping.getColumnName(column)).gte(start).lte(end);
        return this;
    }

    /**
     * or  $or:[{_id:115469},{_id:115471,personnel_number:RegExp('.*15101131856.*')}]
     */
    public CriteriaWrapper orOperator(CriteriaWrapper... criteriaWrappers) {
        criteria.orOperator(Stream.of(criteriaWrappers).map(wrapper -> wrapper.getCriteria()).toArray(Criteria[]::new));
        return this;
    }

    /**
     * 取余运算
     * {_id:115468,channel_code_id:{$mod:[2,0]}}
     *
     * @param column
     * @param value
     * @param remainder
     * @return {@link CriteriaWrapper}
     */
    public <T> CriteriaWrapper mod(SFunction<T, ?> column, Number value, Number remainder) {
        criteria.and(MongoFieldMapping.getColumnName(column)).mod(value, remainder);
        return this;
    }

    /**
     * 完全匹配
     * 在mongodb中 完全匹配 in 和 all , all为完全匹配方式，匹配级别高于in 当前查询中会选中第二个
     * db.col_content.find({'nodeID':{$all:['123','7788']}})
     * { "_id" : ObjectId("525baa67539d1ec00700002a"), "nodeID" : [ "7788" ], "i" : 3 }
     * { "_id" : ObjectId("525baab1539d1ec00700002b"), "nodeID" : [ "123", "7788" ], "i" : 4}
     *
     * @param column
     * @param values
     * @return {@link CriteriaWrapper}
     */
    public <T> CriteriaWrapper all(SFunction<T, ?> column, Object... values) {
        criteria.and(MongoFieldMapping.getColumnName(column)).all(values);
        return this;
    }

    /**
     * $type 查看字段的类型  下为类型对照表
     * Double 1
     * String 2
     * Object 3
     * Array 4
     * Binary data 5
     * Undefined 6 已废弃。
     * Object id 7
     * Boolean 8
     * Date 9
     * Null 10
     * Regular Expression 11
     * JavaScript 13
     * Symbol 14
     * JavaScript (with scope) 15
     * 32-bit integer 16
     * Timestamp 17
     * 64-bit integer 18
     * Min key 255 Query with -1.
     * Max key 127
     *
     * @param column
     * @param typeNumber
     * @return {@link CriteriaWrapper}
     */
    public <T> CriteriaWrapper type(SFunction<T, ?> column, int typeNumber) {
        criteria.and(MongoFieldMapping.getColumnName(column)).type(typeNumber);
        return this;
    }

    /**
     * 数组转换集合
     *
     * @param values
     * @return {@link Collection< Object>}
     */
    private Collection<Object> arrayToCollection(Object... values) {
        List<Object> list = new ArrayList<>();
        for (Object value : values) {
            if (value instanceof Collection collection) {
                list.addAll(collection);
            } else {
                list.add(value);
            }
        }
        return list;
    }

}
